﻿using NopCore = Nop.Core.Domain.Common;
using Nop.Core.Domain.Logistics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.IO;
using Nop.Services.Common;

namespace Logistics.Services.Bing
{
    public class RouteService : RouteServceBase
    {
        // TODO: Reduce request 
        private const string BingKey = "AlRGVamO4u6KozwpcxM07DfoZZVN-UOSDUi2hOsP15cu4TQ8wSsJUcL_lgfo1jM4";

        private string buildRequest(NopCore.Address start, params NopCore.Address[] ends)
        {
            var addresses = new List<string>();
            addresses.Add(start.FormattedAddress());
            addresses.AddRange(ends.Select(a => a.FormattedAddress()));
            return new Uri(MapRouteHelper.BuildDirectionUrlWithKey(addresses.ToArray())).ToString();
        }

        private Route doRequest(string url)
        {
            HttpWebResponse res = MakeRequest(url);

            var _JsonSerializer = new DataContractJsonSerializer(typeof(Response));
            var _Result = (Response)_JsonSerializer.ReadObject(res.GetResponseStream());

            res.Dispose();
            return _Result.ResourceSets[0].Resources[0] as Route;
        }

        private double getDuration(Route r)
        {
            // TODO: Verify
            var rr = r.RouteLegs.Select(l => l.TravelDuration);
            return rr.FirstOrDefault();
        }

        private double getDistance(Route r)
        {
            // TODO: Verify
            var rr = r.RouteLegs.Select(l => l.TravelDistance);
            return rr.FirstOrDefault();
        }

        public override TravelInformation GetTravelInformation(NopCore.Address start, IEnumerable<NopCore.Address> ends)
        {
            // TODO: Verify the input params
            var info = new TravelInformation();
            info.Distances = new Dictionary<NopCore.Address, double>();
            info.Durations = new Dictionary<NopCore.Address, double>();

            foreach (var e in ends)
            {
                string req = buildRequest(start, e);
                Route r;
                try 
	            {	        
		            r = doRequest(req);
	            }
                catch (WebException ex)
	            {
                    string res = null;
                    if (ex.Response != null)
                    {
                        TextReader reader = new StreamReader(ex.Response.GetResponseStream());
                        res = reader.ReadToEnd();
                    }
                    
                    throw new InvalidOperationException(
                                    String.Format("The server cannot process the this request: {0} response: {1}", req, res != null ? res : "N/A"),
                                    ex);
	            }
                
                info.Distances.Add(e, getDistance(r));
                info.Durations.Add(e, getDuration(r));
            }
            return info;
        }
    }
}
